import dayjs from 'dayjs';
import durationPlugin, { type Duration } from 'dayjs/plugin/duration';

dayjs.extend(durationPlugin);

/**
 * 倒计时函数
 *
 * 支持两种停止条件：
 * 1. 达到指定次数（times参数）
 * 2. 达到指定总时长（duration参数）
 *
 * 当同时提供times和duration参数时，优先满足duration时长限制
 *
 * @example
 * ```typescript
 * // 基本用法：每秒1秒执行一次，共执行5次
 * const cancel = cutDown({
 *   times: 5,
 *   callback: (count) => console.log(`第${count}次执行`),
 *   stepDuration: dayjs.duration(1, 'second')
 * });
 *
 * // 立即执行第一次，然后每500ms执行一次
 * const cancel2 = cutDown({
 *   times: 3,
 *   immediate: true,
 *   callback: (count) => console.log(`立即执行: ${count}`),
 *   stepDuration: dayjs.duration(500, 'ms')
 * });
 *
 * // 时长限制：最多执行10秒，即使没有达到times次数
 * const cancel3 = cutDown({
 *   times: 100,
 *   duration: dayjs.duration(10, 'seconds'),
 *   callback: (count) => console.log(`时长限制: ${count}`),
 *   stepDuration: dayjs.duration(200, 'ms')
 * });
 *
 * // runappend模式：达到停止条件后继续执行
 * const cancel4 = cutDown({
 *   times: 3,
 *   runappend: true,
 *   callback: (count) => console.log(`继续执行: ${count}`),
 *   stepDuration: dayjs.duration(1, 'second')
 * });
 *
 * // 手动取消
 * setTimeout(() => {
 *   cancel(); // 停止执行
 * }, 3000);
 * ```
 *
 * @param options 配置选项
 * @param options.stepDuration 倒计时步长，默认1秒，必须大于0
 * @param options.duration 总时长限制，可选，如果提供则会在达到总时长后停止
 * @param options.callback 每次倒计时的回调函数，接收当前次数作为参数
 * @param options.times 倒计时次数，必须大于0
 * @param options.immediate 是否立即执行第一次回调，默认false
 * @param options.runappend 是否在达到停止条件后再执行一次，默认false
 * @returns 取消函数，用于手动停止倒计时
 *
 * @throws {Error} 当times小于等于0时抛出错误
 * @throws {Error} 当stepDuration小于等于0时抛出错误
 */
export const cutDown = (options: {
  /** 倒计时步长 */
  stepDuration?: Duration;
  /**时长 */
  duration?: Duration;
  /** 回调函数 */
  callback?: (times: number) => void;
  /** 倒计时次数 */
  times: number;
  /** 是否立即执行 */
  immediate?: boolean;
  /** 是否最后再执行一次 */
  runappend?: boolean;
}) => {
  let id: ReturnType<typeof setTimeout> | undefined;
  let count = 0;
  let isActive = true;
  const startTime = Date.now();

  const {
    stepDuration: durn = dayjs.duration(1, 's'),
    immediate = false,
    times,
    callback,
    duration: totalDuration,
    runappend = false,
  } = options;

  // 参数验证
  if (times <= 0) {
    throw new Error('times must be greater than 0');
  }

  if (durn.asMilliseconds() <= 0) {
    throw new Error('stepDuration must be greater than 0');
  }

  if (totalDuration && totalDuration.asMilliseconds() <= 0) {
    // duration为0或负数时，立即停止
    isActive = false;
  }

  // 策略函数：检查是否超过时长限制
  const checkDurationLimit = (): boolean => {
    if (!totalDuration) return false;
    const currentTime = Date.now();
    const elapsed = currentTime - startTime;
    return elapsed >= totalDuration.asMilliseconds();
  };

  // 策略函数：检查是否超过次数限制
  const checkTimesLimit = (): boolean => {
    return count >= times;
  };

  // 策略函数：处理时长限制超时情况
  const handleDurationExceeded = () => {
    clearTimer();
    if (runappend && isActive) {
      count++;
      executeCallback(count);
    }
  };

  // 策略函数：处理次数限制达到情况
  const handleTimesReached = () => {
    clearTimer();
    if (runappend && isActive) {
      scheduleNext();
    }
  };

  const clearTimer = () => {
    if (id !== undefined) {
      clearTimeout(id);
      id = undefined;
    }
  };

  const executeCallback = (currentCount: number): boolean => {
    if (!callback) return true;

    try {
      callback(currentCount);
      return true;
    } catch (error) {
      console.error('Error in cutDown callback:', error);
      clearTimer();
      return false; // 返回false表示执行失败，停止后续执行
    }
  };

  const scheduleNext = () => {
    if (isActive) {
      id = setTimeout(tick, durn.asMilliseconds());
    }
  };

  const tick = () => {
    if (!isActive) return;

    // 优先检查时长限制（在执行回调前检查）
    if (checkDurationLimit()) {
      handleDurationExceeded();
      return;
    }

    // 执行正常回调
    count++;
    const success = executeCallback(count);
    if (!success) {
      // 如果回调失败，停止后续执行
      return;
    }

    // 检查次数限制
    if (checkTimesLimit()) {
      handleTimesReached();
    } else {
      // 继续调度下一次
      scheduleNext();
    }
  };

  // 初始化执行
  if (immediate && isActive) {
    count++;
    const success = executeCallback(count);
    if (!success) {
      // 如果立即执行失败，停止所有后续执行
      return () => {
        isActive = false;
        clearTimer();
      };
    }

    // 检查是否需要继续执行
    if (!checkDurationLimit() && !checkTimesLimit()) {
      scheduleNext();
    } else if (runappend && checkTimesLimit() && !checkDurationLimit()) {
      // 如果达到次数但未超时，且需要runappend
      scheduleNext();
    }
  } else if (isActive) {
    scheduleNext();
  }

  return () => {
    isActive = false;
    clearTimer();
  };
};
